SERVEI DE CONFIGURACIÓ







Introducció

Propósit

El Servei de Configuració té com a propósit la configuració de les propietats de qualsevol component de l'aplicació. Aquestes propietats poden ser tant referències a altres objectes com propietats internes (atributs) que necessiten per al seu correcte funcionament.

Aquesta configuració es pot realitzar de 2 formes diferenciades:

  1. Forma tradicional

Una de les formes tradicionals d'estructurar el nostre codi és que siguin els components qui realitzin la localització i instanciació dels serveis o components que han de cridar dins la seva lógica (així com l'obtenció de les propietats internes)

En aquesta aproximació, encara utilitzada però poc pràctica, es pot fer ús del patró factory per amagar la complexitat de la instanciació i obtenció de les instàncies requerides. Tot i això, aquest mecanisme l'han de realitzar els clients, els quals han de conéixer quines dependències tenen els components entre sí i per tant conéixer quina seqüència han de seguir per la inicialització de cadascun d'ells.

En altres casos, l'especificació J2EE fa ús de JNDI com a mecanisme d'obtenció de referències d'objectes. Aquesta implementació requereix molts canvis si es fa un canvi d'entorn i deixa d'usar-se JNDI.

  1. Patró 'Dependency Injection'

Mitjançant l'ús del patró Dependency Injection el nostre aplicatiu només ha de definir les dependències, i deixar que un codi extern (el framework) s'encarregui de la complexitat d'instanciació, inicialització i seqüència de les referència que requereixin els nostres components. Aquest patró elimina totes les problemàtiques de la forma tradicional.

canigo es basa en l'ús del patró 'Dependency Injection' mitjançant l'ús de Spring. El més important és que el seu ús no és intrusiu, ja que en qualsevol moment podríem canviar de mecanisme intern sense afectar el nostre codi.

Context i Escenaris d'Ús

El Servei de Configuració es troba dins dels serveis de Propósit General de canigo.
El seu ús és imprescindible en canigo, ja que tots els serveis utilitzen la gestió de configuració definida per aquest servei.

Versions i Dependències

Les dependències descrites a la següent url son requerides per tal de compilar i fer funcionar el projecte:
Dependències Servei de Configuració

A qui va dirigit

Es recomana una lectura prèvia de http://static.springframework.org/spring/docs/1.2.x/reference/beans.html

Documents i Fonts de Referència

Per a començar a fer ús del Servei de Configuració és imprescindible la lectura dels següents documents:

Nom Descripció
Configuració amb Spring http://static.springframew/
http://static.springframew/static.springframework.org/spring/docs/1.2.x/reference/beans.html

I es recomana la lectura de les següents referències:

Nom Descripció
Introducció patró 'Dependency Injection' http://www.theserverside.com/articles/article.tss?l=IOCBeginners
Definició del patró 'Dependency Injection' http://www.martinfowler.com/articles/injection.html

Glossari

Dependency Injection

Patró de disseny que estableix un nivell d'abstracció mitjançant la definició de interfícies i eliminant la dependència entre components mitjançant un codi extern que injecta aquestes dependències de forma transparent. Existeixen 3 formes del patró: 'setter', 'constructor' i 'interface based injection'.

Descripció Detallada

Arquitectura i Components

Els components podem classificar-los en:

  1. Interfícies i Components Genérics. Interfícies del servei i components d'ús general amb independència de la implementació escollida.
  2. Implementació basada en Spring

Es pot trobar tota la documentació JavaDoc y el codi font referent aquests components a les següents urls:

JavaDoc: http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-configuration/apidocs/index.html
Codi Font:  http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-configuration/xref/index.html

Instal- lació i Configuració

Instal- lació

La instal- lació del servei requereix de la utilització de la llibreria 'canigo-services-configuration' i les dependències indicades a l'apartat 'Introducció-Versions i Dependències'.

Configuració

La configuració del Servei de Configuració es realitza principalment amb el mecanisme d'injecció utilitzat. En el cas de Spring consultar http://static.springframework.org/spring/docs/1.2.x/reference/beans.html.

A més de la configuració ja existent mitjançant el mecanisme d'injecció base (Spring) s'ofereixen les següents facilitats:

  1. Definir un configurador que obtingui diferents valors de propietats segons la màquina en la que instal- lem l'aplicatiu
  2. Definir un configurador per obtenir variables de l'entorn del sistema
  1. Consideració amb Spring

En cap lloc s'especificarà quin és el servei de configuració que es fa servir per l'obtenció dels components i la seva injecció. Spring, per defecte cercarà totes les configuracions que s'hagin definit amb les classes configuradores (PropertyPlaceHolderConfigurer,...).

Configuració segons màquina

Atributs:

Atributs Requerit Descripció
class Implementació concreta del configurador a utilitzar

Usar el valor:
# net.gencat.ctti.canigo.services.configuration.springframework.beans.
factory.config.HostPropertyPlaceholderConfigurer


Si es volen definir varis fitxers de configuració

# net.gencat.ctti.canigo.services.configuration.springframework.beans.factory.
config.HostPropertyResourceConfigurer


Si es vol definir un únic fitxer de configuració
  1. HostPropertyResourceConfigurer

Propietats:

Propietat Requerit Descripció
basePropertyFile Fitxer de configuració amb la definició dels beans i les seves propietats
Per obtenir el fitxer de configuració s'obté el path definit en aquesta propietat i se li afegeix com a sufix el DNS de la màquina. És a dir, si s'ha configurat el fitxer 'nomFitxer.ext' es cercarà el fitxer 'nomFitxer.ext.nomHost'.
  1. HostPropertyPlaceholderConfigurer

Propietats:

Propietat Requerit Descripció
basePropertyFiles Llista de fitxers de configuració amb la definició dels beans i les seves propietats

Per obtenir el fitxer de configuració s'obté el path definit en aquesta propietat i se li afegeix com a sufix el DNS de la màquina. És a dir, si s'ha configurat el fitxer 'nomFitxer.ext' es cercarà el fitxer 'nomFitxer.ext.nomHost'.

Exemple:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
	<bean id="configurationService"class="net.gencat.ctti.canigo.services.
	configuration.springframework.beans.factory.config.HostPropertyPlaceholderConfigurer">
		<property  name="basePropertyFiles">
			<list>
				<value>classpath:jdbc/jdbc.properties</value>
			</list>
		</property>
	</bean>
</beans>






En el cas a dalt mostrat, si el DNS de la màquina és 'ES-57PYM1J' es cercarà el fitxer de configuració 'jdbc/jdbc.properties.ES-57PYM1J'. En cas que no es trobi aquest fitxer es cercaria el fitxer ' jdbc/jdbc.properties'.

Configuració de variables del sistema

Ús de múltiples fitxers de configuració

Sovint és útil dividir el conjunt de definicions en múltiples fitxers XML. Mitjançant alguns dels configuradors és possible referenciar més d'un fitxer.

Tot i això és possible que volguem repartir els fitxers en diferents parts. En aquest cas podem fer ús de l'element 'import'.

<beans>
<import resource="services.xml"/>
<import  resource="resources/messageSource.xml"/>
<import  resource="/resources/themeSource.xml"/>
<bean id="bean1"  class="..."/>
<bean id="bean2" class="..."/>
. . .







En aquest exemple, les definicions externes de beans s'estan carregant des de 3 fitxers, services.xml, messageSource.xml i themeSource.xml. Tots els paths són considerats relatius al fitxer de definició que realitza la importació, per tant en aquest cas services.xml ha d'estar en el mateix directori o classpath location que el fitxer que realitza la importació, mentres que messageSource.xml i themeSource.xml han d'estar en el directori resources per sota del fitxer que realitza la importació. Es pot comprovar que una barra inicial és ignorada, però atés que aquests paths són relatius, probablement és millor no usar la barra. Els continguts dels fitxers importats han de ser definicions XML de beans completament vàlides d'acord amb el DTD, incloent l'element bean de més alt nivell.





Utilització del Servei

Degut a que la configuració dels elements de l'aplicació o dels serveis es realitza de forma externa mitjançant el patró 'Dependency Injection' no és convenient l'obtenció de propietats des dels clients. Aquesta obtenció es realitza de forma transparent fora de les classes de l'aplicació.

Obtenció de valors de configuració des de les classes

Si en algun cas es requereix de l'obtenció de propietats externes des del nostre codi (opció no recomanada), podem fer ús de la interfície 'ConfigurationService'.

package net.gencat.ctti.canigo.services.configuration;
...
public interface ConfigurationService {
	public String getProperty(String key) throws ConfigurationServiceException;
}






Es proporciona una implementació basada en Spring, anomenada 'HostPropertyPlaceholderConfigurer'

El mètode que s'exposa és un i es correspon a la següent signatura:

  •     public String getProperty(String key): Demana un valor de configuració amb la clau key.

Si el valor de configuració no es troba, es llença una excepció de tipus 'ConfigurationServiceException' indicant que el valor que es busca no s'ha trobat.

Per a utilitzar-lo, només cal que afegim al fitxer XML de definició de beans el nostre servei de configuració i injectem als nostres beans el servei de configuració, tal i com es mostra a continuació:

...
    <bean  id="myBean" class="com.myapp.model.MyBean">
        <property  name="configService" ref="configurationService" />
    </bean>

...
    <bean id="configurationService" class="net.gencat.ctti.canigo.
    services.configuration.springframework.beans.
    factory.config.HostPropertyResourceConfigurer.">
        <property  name="basePropertyFiles">
            <list>
                <value>classpath:config.properties</value>
            </list>
        </property>
    </bean>
...





  1. Referència al servei de configuració des del nostre bean
  2. Implementació del servei de configuració escollida
  3. Path del fitxer de propietats

I des de la classe que vol fer ús del servei:

...
public class MyBean {
	private ConfigurationService configService = null;
	public String myMethod() throws Exception {
		String configValue = configService.getProperty("config.value");*
		...
	}
}






Integració amb Altres Serveis

El Servei de Configuració és usat per tots els Serveis per tal de definir les seves propietats de forma externa.

En comptats casos, algunes de les implementacions sobre el que es troben els serveis de canigo no permeten la definició de les seves propietats mitjançant la injecció.

Servei de Traces basat en Log4J

En el Servei de Traces canigo ofereix la possibilitat de configurar diferents fitxers segons el host en el que es troba l'aplicació mitjançant la classe 'net.gencat.ctti.canigo.services.logging.log4j.xml.HostDOMConfigurator'. Per a més referència consultar el document 'Servei de Traces'.

Exemples

Exemple de Configuració

Imaginem que tenim un DAO que fa ús d'una factoria de sessions per llençar els objectes de negoci contra una capa de persistència i persistir les dades. Aquesta relació es representa en la següent figura:


Aquests elements i relacions es representen amb un fitxer XML amb una estructura definida. En el nostre exemple, tindríem un fitxer XML amb la següent informació:



  1. Element 'myDao'
  2. Element 'sessionFactory'
  3. Relació entre 'myDao' i 'sessionFactory'

A continuació es mostra aquest fitxer:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
	<!--Element estructural que representa la factoria de sessions-->
	<bean name="sessionFactory"  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property  name="configLocation" value="classpath:hibernate.cfg.xml"  />
	</bean>
	<!--Element estructural que representa el DAO-->
	<bean id="myDao"  class="test.dao.hibernate.impl.HibernateCategoryDAOImpl">
		<!--Relació entre el nostre DAO i la factoria de sessions-->
		<property  name="sessionFactory" ref="sessionFactory" />
	</bean>
</beans>





Tal i com defineix Spring, cada 'bean' representa un element o actor del nostre aplicatiu i es representa al XML com un node <bean/>. Els valors més importants a configurar en aquest node són:

  1. Atribut 'name': nom amb el que identifiquem el 'bean'
  2. Atribut 'class': nom de la classe que s'instanciarà quan es pregunti o resolgui el bean.
  3. Atributs de comportament: indiquen cóm ha de comportar-se el 'bean' en el contenidor. En l'exemple presentat es veu un cas molt senzill, en el que els elements representats són instàncies úniques en l'aplicatiu. Això i altres aspectes són completament configurables però s'escapen de l'objectiu d'aquest document (per exemple, si volem configurar que cada vegada que es demani un 'bean' pel 'name' es retorni una instància diferent, es farà servir l'atribut 'singleton'). També existeixen atributs per indicar mètodes d'inicialització, 'init-method' i destrucció 'destroy-method').

Per a una explicació en més detall dels diferents atributs del node <bean/> consultar la url: http://static.springframework.org/spring/docs/1.2.x/reference/beans.html.

A part els atributs del node <bean/>, aquest pot tenir subnodes, que principalment representen altres 'beans' que necessita per a treballar, és a dir, els col.laboradors. En el nostre exemple hem definit un node <property/> amb el nom 'sessionFactory' que fa referència al col.laborador: el 'bean' amb nom 'sessionFactory' que representa la factoria de sessions..

El nom de les <property/> es correspon amb els noms de les variables d'instància del propi 'bean'

La definició d'aquests fitxers de configuració XML servirà posteriorment per anar a buscar en l'aplicatiu, en el cas que sigui necessari, les instàncies dels beans corresponents. Hi haurà doncs un procès automàtic que llegirà aquests fitxers i farà de punt d'entrada per a qualsevol petició de 'bean'. Aquest procès l'executa una entitat que es coneix com a 'BeanFactory'. L'existència d'aquesta factory en una aplicació J2EE bé representada per una classe que es diu 'WebApplicationContext'.

??* Nota:

L'existència d'aquesta factory és totalment transparent al desenvolupador, ja que no s'ha de preocuperar de conèixer, a no ser que sigui necessari, de l'existència de la mateixa, si no de la correcta configuració dels XML per a que aquesta resolgui les dependències entre col.laboradors correctament.

Exemple de Configuració amb Definició segons l'Entorn

En l'anterior exemple hem vist la forma de configurar la nostra aplicació en funció dels fitxers XML que representen els nostres 'beans' i les seves relacions. Però hi han aspectes que s'escapen en aquesta configuració:

  1. Definició de propietats de 'beans' segons l'entorn de l'aplicació
  2. Obtenció de valors de configuració que no pertànyin a cap bean

Definició de Propietats segons Entorn

Imaginem que en l'escenari que hem presentat a la Figura 1 i afegim un 'dataSourceBean' que fa de factoria de connexions i que té una relació de col.laboració amb la factoria de sessions:


Al fitxer de configuració abans presentat s'afegeix el següent bean:

...
<bean id="dataSourceBean"  class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property  name="driverClassName" value="$\{jdbc.driverClassName\}"  />
<property name="url" value="$\{jdbc.url\}" />
<property  name="username" value="$\{jdbc.username\}" />
<property name="password"  value="$\{jdbc.password\}" />
</bean>
...





i es modifica la configuració del bean 'sessionFactory' per afegir la relació de col.laboració:

<property name="dataSource" ref="dataSourceBean" />



Si ens fixem en les propietats del bean 'dataSourceBean' veurem que hi han quatre <property/> sensibles de canviar segons l'entorn:

  1. driverClassName
  2. url
  3. username
  4. password

Els valors d'aquests atributs s'extreuen d'un fitxer extern segons la definició '${jdbc.driverClassName},${jdbc.url},${jdbc.username} i ${jdbc.password}. Però, d'ón es treuen els valors d'aquestes variables?

En aquest punt intervé el servei de configuració: mitjançant les classes 'HostPropertyPlaceholderConfigurer' i 'HostPropertyResourceConfigurer' que trobem en el package 'net.gencat.ctti.canigo.services.configuration.springframework.beans.factory.config' es configura quina és la localització del fitxer extern.

...
<bean  id="dataSourceBean" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property  name="driverClassName" value="$\{jdbc.driverClassName\}"  />
	<property name="url" value="$\{jdbc.url\}" />
	<property  name="username" value="$\{jdbc.username\}" />
	<property name="password"  value="$\{jdbc.password\}" />
</bean>...
<bean  id="configurationService" class="net.gencat.ctti.canigo.services.configuration.
	springframework.beans.factory. config.HostPropertyPlaceholderConfigurer">
	<list>
		<value>classpath:config.propertiesc</value>	</list>
</bean>
...





Configuració del fitxer extern
Aquest fitxer extern defineix els valors de les propietats:

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:  D:/path_to_db/testDB
jdbc.username=sa
jdbc.password=